Skip to content

{Feature} Core - Decode EMG samples and add a numpy accessor in PAT - #364

Open
SeaOtocinclus wants to merge 1 commit into
facebookresearch:mainfrom
SeaOtocinclus:export-D110117191
Open

{Feature} Core - Decode EMG samples and add a numpy accessor in PAT#364
SeaOtocinclus wants to merge 1 commit into
facebookresearch:mainfrom
SeaOtocinclus:export-D110117191

Conversation

@SeaOtocinclus

Copy link
Copy Markdown
Contributor

Summary:
Explanation:
Project Aria Tools previously exposed each EMG sample only as a raw packed byte blob (EmgImuSample.packed_channel_data), forcing every consumer to reimplement the same big-endian / offset-binary / sample-major unpacking. This moves the decode into PAT so callers get decoded samples directly.

  • New decodeEmgSamples(const EmgData&) in core/data_provider/players/EmgPlayer.{h,cpp} returns a DecodedEmgSamples struct (row-major [num_sub_samples, channel_count] raw ADC counts). It unpacks each EmgData.emg blob (big-endian, unsigned 16-bit, offset-binary, sample-major), skips malformed blobs, and throws std::invalid_argument for an unsupported bit depth or a non-zero encoding.
  • Python: EmgData.get_emg_samples() and the module-level sensor_data.decode_emg_samples(emg_data) return the decoded batch as a [num_sub_samples, channel_count] uint16 numpy array (core/python/SensorDataPyBind.h; stub in core/python/internal/stubs/sensor_data.pyi).

No physical-unit (microvolt) conversion is provided: Aria Gen 2 recordings carry no EMG calibration (the EMG stream's configuration calibration field is empty, and the device factory-calibration JSON has no EMG section), so values remain raw ADC counts and must be treated as relative.

Reproducibility:
Build and run the new C++ and Python unit tests (see Test Plan). The decoded array equals an independent big-endian uint16 reshape of the packed blobs.

Differential Revision: D110117191

Summary:
Explanation:
Project Aria Tools previously exposed each EMG sample only as a raw packed byte blob (`EmgImuSample.packed_channel_data`), forcing every consumer to reimplement the same big-endian / offset-binary / sample-major unpacking. This moves the decode into PAT so callers get decoded samples directly.

- New `decodeEmgSamples(const EmgData&)` in `core/data_provider/players/EmgPlayer.{h,cpp}` returns a `DecodedEmgSamples` struct (row-major `[num_sub_samples, channel_count]` raw ADC counts). It unpacks each `EmgData.emg` blob (big-endian, unsigned 16-bit, offset-binary, sample-major), skips malformed blobs, and throws `std::invalid_argument` for an unsupported bit depth or a non-zero encoding.
- Python: `EmgData.get_emg_samples()` and the module-level `sensor_data.decode_emg_samples(emg_data)` return the decoded batch as a `[num_sub_samples, channel_count]` `uint16` numpy array (`core/python/SensorDataPyBind.h`; stub in `core/python/internal/stubs/sensor_data.pyi`).

No physical-unit (microvolt) conversion is provided: Aria Gen 2 recordings carry no EMG calibration (the EMG stream's configuration calibration field is empty, and the device factory-calibration JSON has no EMG section), so values remain raw ADC counts and must be treated as relative.

Reproducibility:
Build and run the new C++ and Python unit tests (see Test Plan). The decoded array equals an independent big-endian `uint16` reshape of the packed blobs.

Differential Revision: D110117191
@meta-cla meta-cla Bot added the cla signed label Jun 30, 2026
@meta-codesync

meta-codesync Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

@SeaOtocinclus has exported this pull request. If you are a Meta employee, you can view the originating Diff in D110117191.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants